using System;
using System.Timers;
using System.Runtime.InteropServices;
using System.Threading;
using System.Drawing;
using System.Collections.Generic;
using Clandestine.Base;

namespace Clandestine.Graphics.Animation
{
    internal class Animation
    {
    	public Graphics Graphics;
        public Texture[] Frames = null;
        public int CurrentFrame = 0;
        public EventWaitHandle FinishedAnimation = new EventWaitHandle(false, EventResetMode.ManualReset);
        public bool Loop = true;
        public string Name = string.Empty;
        
        private System.Timers.Timer tmr = new System.Timers.Timer();

        public bool Playing
        {
            get { return tmr.Enabled; }
            set { tmr.Enabled = value; }
        }
        
        /// <summary>
        /// Time between frames, in ms.
        /// </summary>
        public double Interval
        {
            get { return tmr.Interval; }
            set { tmr.Interval = value; }
        }

        public int FrameCount
        {
            get { return Frames.Length; }
        }

        internal Animation(Graphics graphics, string name, double interval, bool loop)
        {	
        	this.Graphics = graphics;
            this.Name = name;
            this.Interval = interval;
            this.Loop = loop;

            // Timer
            tmr.AutoReset = true; // might be default, idk.
            tmr.Elapsed += new ElapsedEventHandler(Poll);
        }

        public Animation(Graphics graphics, string name, double interval, bool loop, string spritesheet, Bitmap bmp, int gridWidth, int gridHeight, int rowStart, int colStart, int rowEnd, int colEnd)
            : this(graphics, name, interval, loop)
        {
            this.AddFrames(spritesheet, bmp, gridWidth, gridHeight, rowStart, colStart, rowEnd, colEnd);
        }

        public void AddFrames(string spritesheet, Bitmap bmp, int gridWidth, int gridHeight, int rowStart, int colStart, int rowEnd, int colEnd)
        {
            List<Texture> frames = new List<Texture>(); // Lists<> are easier than calculating how many frames..
            Rectangle destR = new Rectangle(0, 0, gridWidth, gridHeight); // Size of one frame.

            // How many 'tiles' per row
            int tilesPerRow = bmp.Width / gridWidth;
            
            // Produce a warning for funny sizes - more trouble than it's worth?
            int xQ = 0, yQ = 0;
            Math.DivRem(bmp.Width, gridWidth, out xQ);
            Math.DivRem(bmp.Height, gridHeight, out yQ);
            if ((xQ > 0) || (yQ > 0))
                Log.w("Animation: Warning, the sprite sheet, \"{0}\", is a peculiar size.", spritesheet);

            // Cut out tiles
            for (int y = rowStart; y <= rowEnd; y++)
            {
                for (int x = ((y == rowStart) ? colStart : 0); x <= ((y == rowEnd) ? colEnd : tilesPerRow); x++)
                {
                    Bitmap buffer = new Bitmap(gridWidth, gridHeight);
                    System.Drawing.Graphics g = System.Drawing.Graphics.FromImage((Image)buffer);

                    // Figure out portion of the spritesheet to cut out
                    // Beware! Rect is Rectangle(x, y, width, height) who knew.
                    Rectangle sourceR = new Rectangle(x * gridWidth, y * gridHeight, gridWidth, gridHeight);

                    // Cut it out and make into a texture.
                    g.DrawImage((Image)bmp, destR, sourceR, GraphicsUnit.Pixel);
                    frames.Add(Graphics.Textures.AddBitmap(String.Format("{0}!{1}!!{2}x{3}", spritesheet, this.Name, x, y), buffer));
                }
            }
            Frames = frames.ToArray();
        }

        public Animation Clone()
        {
            /*Animation a = new Animation(this.Graphics, this.Name, this.Interval, this.Loop);
            
            // Copy textures across, ref counts!
            a.Frames = new Texture[this.Frames.Length];
            for (int i = 0; i < this.Frames.Length; i++)
            	a.Frames[i] = this.Frames[i].Clone();
            	
            return a;*/
            throw new NotImplementedException("Todo.");
        }

        private void Poll(object sender, ElapsedEventArgs e)
        {
            int cf = CurrentFrame; // prevent race condition

            cf++;
            if (cf >= FrameCount)
            {
                if (!Loop)
                {
                    Playing = false;
                    cf--;
                    FinishedAnimation.Set();
                }
                else
                    cf = 0;
            }

            // Copy result fo' realz.
            CurrentFrame = cf;
        }

        public void Dispose()
        {
        	for (int i = 0; i < Frames.Length; i++)
        		Frames[i].Dispose(); 
            tmr.Dispose();
        }

    }
}

